home *** CD-ROM | disk | FTP | other *** search
- /******************************************************************************
- * A2DiskDump - A utility for extracting all files from an Apple II DOS 3.3
- * disk image and dumping them into individual files.
- *
- * $Header: Big:Archives/Apple II/A2DiskDump/RCS/A2DiskDump.c,v 1.9 2000/02/27 16:38:11 AGMS Exp $
- *
- * Implemented by Alexander G. M. Smith, Ottawa Canada, agmsmith@achilles.net,
- * agmsmith@bix.com, 71330.3173@compuserve.com, and various other places.
- *
- * This code is put into the public domain by AGMS, so you can copy it,
- * hack it up, sell it, and do whatever you want to it.
- *
- * Compile with the SAS C compiler version 6.58, using 32 bit integers.
- *
- * Tested under AmigaDOS 2.1, 68030. Should work on 68000 systems too.
- *
- * $Log: A2DiskDump.c,v $
- * Revision 1.9 2000/02/27 16:38:11 AGMS
- * Mention random access sparse files.
- *
- * Revision 1.8 2000/02/27 13:08:41 AGMS
- * Made quiet mode the default. Also better printing of deleted files.
- *
- * Revision 1.7 2000/02/17 17:18:22 AGMS
- * Added option to convert text files to readable form, and to
- * only process text files.
- *
- * Revision 1.6 2000/02/17 16:21:26 AGMS
- * Made program name a bit shorter.
- *
- * Revision 1.5 2000/02/14 10:44:44 AGMS
- * First working version.
- *
- * Revision 1.4 2000/02/12 16:56:23 AGMS
- * Under construction.
- *
- * Revision 1.3 2000/02/07 17:39:08 AGMS
- * Under construction.
- *
- * Revision 1.2 2000/02/07 16:03:02 AGMS
- * Command line parsing is done, now working on reading the catalog.
- *
- * Revision 1.1 2000/02/07 12:33:56 AGMS
- * Initial revision
- */
-
- #ifdef __SASC
- #define __USE_SYSBASE 1
- /* Need this to make the exec.library headers use the library base global
- variable SysBase in SAS C (otherwise it just uses location $4). */
- #endif
-
- #include <proto/utility.h>
- #include <proto/dos.h>
-
- #include <ctype.h>
- #include <stdlib.h>
- #include <stdio.h>
- #include <string.h>
-
- #ifndef WORKBENCH_STARTUP_H
- #include <workbench/startup.h>
- #endif
-
-
-
- /******************************************************************************
- * Command line argument parsing stuff.
- */
-
- static const char RDArgsTemplate [] =
- "DISKIMAGE/A,DUMPFILES/S,NAMEPREFIX,DROPHEADER/S,KEEPTAIL/S,CONVERTTEXT/S,TEXTFILESONLY/S,NOCOMMENT/S,VERBOSE/S";
- /* Template for parsing the command line, native Amiga style. */
-
- enum RDArgsEnum {
- DISKIMAGE_RDARGINDEX = 0,
- DUMPFILES_RDARGINDEX,
- NAMEPREFIX_RDARGINDEX,
- DROPHEADER_RDARGINDEX,
- KEEPTAIL_RDARGINDEX,
- CONVERTTEXT_RDARGINDEX,
- TEXTFILESONLY_RDARGINDEX,
- NOCOMMENT_RDARGINDEX,
- VERBOSE_RDARGINDEX,
- NUMRDARGS
- };
- /* Argument numbers corresponding to the command line parsing template. */
-
-
- char InputName [256];
- /* Name of the file to use for the disk image. */
-
- BOOL DumpFiles;
- /* TRUE to dump out the files, FALSE if you just want a catalog listing. */
-
- char NamePrefix [256];
- /* This string is pre-pended to the file names being dumped. Usually
- it will be a directory name, but it can be an arbitrary prefix too. */
-
- BOOL DropHeader;
- /* TRUE if the user doesn't want the extra 4 bytes of start address and
- length in Apple binary files. FALSE (the default) leaves the bytes in.
- Only affects binary files, not BASIC etc. */
-
- BOOL KeepTail;
- /* TRUE if the user wants to keep the extra garbage bytes past the normal
- end of the file which fill the last disk sector in the file. The default
- of FALSE doesn't write the extra data (stops at the first NUL in text
- files and stops after the length in binary files and BASIC files). */
-
- BOOL ConvertText;
- /* If TRUE then text files will have the high bit stripped and the
- carriage return converted to a line feed. */
-
- BOOL TextFilesOnly;
- /* If TRUE then the dump will only dump text files, skipping over
- binary and all other file types. */
-
- BOOL NoComment;
- /* TRUE to not write the file type, size, length etc. into the file's comment
- field, FALSE for the default of having a comment. */
-
- BOOL Verbose;
- /* TRUE to turn on extra progress messages, FALSE in quiet mode. */
-
-
- const char HelpText [] =
- "A2DiskDump is a utility for extracting all files from an Apple II\n"
- "DOS 3.3 disk image file (like the kinds made from ADT the Apple Disk\n"
- "Transfer utility). The files are written to the current directory,\n"
- "using the name from the disk's catalog, with illegal characters\n"
- "replaced. By default, a comment is attached to each file with its\n"
- "catalog listing plus secret info (file length, start address).\n"
- "\n"
- "Public domain by Alexander G. M. Smith, Ottawa Canada, send questions to\n"
- "agmsmith@achilles.net, agmsmith@bix.com, 71330.3173@compuserve.com.\n"
- "$VER: A2DiskDump 1.0 " __AMIGADATE__ " ($Id: A2DiskDump.c,v 1.9 2000/02/27 16:38:11 AGMS Exp $)\n"
- "\n"
- "DISKIMAGE/A is the required path name to the disk image file, which\n"
- "is from an Apple II DOS 3.3 disk (256 bytes per sector, 16 sectors per\n"
- "track, 35 tracks) and is 143360 bytes long.\n"
- "\n"
- "DROPHEADER/S is a switch which if present, leaves out the header info\n"
- "in binary files. Otherwise, binary files will have a 4 byte header\n"
- "containing address low/high bytes followed by length low/high. Text\n"
- "and other file types aren't affected by this switch.\n"
- "\n"
- "KEEPTAIL/S is a switch which when present, includes the garbage past\n"
- "the end of the file (end of file determined by the header for binary\n"
- "files and BASIC files, or first NUL in text files). This is useful\n"
- "for random access text files used in databases, where the file contains\n"
- "NUL characters for fixed length record padding. Yes, the code does\n"
- "handle sparse files, filling in the non-existent sectors with zeroes.\n"
- "\n"
- "CONVERTTEXT/S when present switches on conversion of text files from\n"
- "Apple II format into text file format (high bit set to zero and\n"
- "carriage return characters turned into line feeds).\n"
- "\n"
- "TEXTFILESONLY/S makes the dumpfile feature only dump text files,\n"
- "skipping over all other file types.\n"
- "\n"
- "NOCOMMENT/S specify this switch if you don't want to have comments\n"
- "attached to the files. The default is to have comments, with the\n"
- "comment text being much like an Apple II catalog listing.\n"
- "\n"
- "VERBOSE/S switches on debug messages and extra status info etc.\n"
- "\n";
-
-
-
- /******************************************************************************
- * Structures defining the layout of the data on an Apple DOS 3.3 disk.
- */
-
- typedef struct TSPairStruct
- {
- unsigned char track;
- unsigned char sector;
- } TSPair;
-
-
- typedef struct VTOCStruct /* Volume table of contents at track $11, sector 0 */
- {
- char filler1 [1];
- TSPair firstDirectory;
- unsigned char dosVersion;
- char filler2 [2];
- unsigned char volumeNumber;
- char filler3 [45];
- unsigned char tracksPerDisk;
- unsigned char sectorsPerTrack;
- unsigned char bytesPerSectorLowByte;
- unsigned char bytesPerSectorHighByte;
- unsigned long sectorsAllocatedInTrackMask [35];
- } VTOC;
-
-
- typedef struct FileEntryStruct
- {
- TSPair firstTrackSectorList;
- unsigned char fileType;
- char fileName [30];
- unsigned char numberOfSectorsUsedLowByte;
- unsigned char numberOfSectorsUsedHighByte;
- } FileEntry;
-
-
- typedef struct DirectoryStruct /* Directory sector */
- {
- char filler1 [1];
- TSPair nextDirectory;
- char filler2 [8];
- FileEntry fileEntry[7];
- } Directory;
-
-
- typedef struct TrackSectorListStruct
- {
- char filler1 [1];
- TSPair nextTSList;
- char filler2 [2];
- unsigned char fileOffsetLow;
- unsigned char fileOffsetHigh;
- char filler3 [5];
- TSPair list[122];
- } TrackSectorList;
-
-
- typedef struct BinaryFileHeaderStruct /* Start of a binary file */
- {
- unsigned char addressLow;
- unsigned char addressHigh;
- unsigned char lengthLow;
- unsigned char lengthHigh;
- } BinaryFileHeader;
-
-
- typedef struct BasicFileHeaderStruct /* Start of a BASIC (INT and FP) file */
- {
- unsigned char lengthLow;
- unsigned char lengthHigh;
- } BasicFileHeader;
-
-
-
- /******************************************************************************
- * Things too large to be on the stack or that are global.
- */
-
- char ErrorMessage [256];
- /* For formatting error messages into. */
-
- FILE *InputFile;
- /* The disk image input file. NULL if closed. */
-
- FILE *OutputFile;
- /* The currently open output file. NULL if closed. */
-
- __far char DiskImage [35 * 16 * 256];
- /* The whole disk image file gets read into this array for processing.
- It is declared as __far since it is more than 32K and thus won't fit
- in the near data section and thus uses full 32 bit addresses rather
- than register A4 relative with a 16 bit offset. */
-
- int VolumeNumber;
- /* The volume number of the disk. */
-
- #define MAX_COMPLETE_TSLIST_SIZE 65536
-
- __far TSPair CompleteSectorList [MAX_COMPLETE_TSLIST_SIZE];
- /* A list of all the track/sectors in a particular file. This array
- is actually bigger than all the memory in an Apple II! */
-
- int NumberOfSectorsInCompleteSectorList;
- /* Number of elements in the CompleteSectorList array. */
-
- __far char ZeroesBuffer [256];
- /* A buffer full of zeroes for writing into unallocated sectors. */
-
-
-
- /******************************************************************************
- * Look at the argument values the user specified and try to make sense of
- * them. Also generates defaults for unspecified settings.
- */
-
- BOOL ExamineArguments (LONG ArgumentValues [NUMRDARGS])
- {
- /* Get the disk image path name. */
-
- strncpy (InputName,
- (char *) ArgumentValues [DISKIMAGE_RDARGINDEX],
- sizeof (InputName));
-
- if (InputName[0] == 0)
- {
- printf ("Need a longer disk image file name.\n");
- return FALSE;
- }
-
- if (ArgumentValues [NAMEPREFIX_RDARGINDEX] != 0)
- strncpy (NamePrefix,
- (char *) ArgumentValues [NAMEPREFIX_RDARGINDEX],
- sizeof (NamePrefix));
- else
- NamePrefix[0] = 0;
-
- /* Read the various switch arguments. */
-
- DumpFiles = (ArgumentValues [DUMPFILES_RDARGINDEX] != 0);
- DropHeader = (ArgumentValues [DROPHEADER_RDARGINDEX] != 0);
- KeepTail = (ArgumentValues [KEEPTAIL_RDARGINDEX] != 0);
- ConvertText = (ArgumentValues [CONVERTTEXT_RDARGINDEX] != 0);
- TextFilesOnly = (ArgumentValues [TEXTFILESONLY_RDARGINDEX] != 0);
- NoComment = (ArgumentValues [NOCOMMENT_RDARGINDEX] != 0);
- Verbose = (ArgumentValues [VERBOSE_RDARGINDEX] != 0);
-
- if (Verbose)
- printf (
- "A2DiskDump compiled on " __DATE__ " at " __TIME__ ".\n"
- "Disk image input file: \"%s\"\n"
- "Dump Files: %s\n"
- "Output file name prefix: \"%s\"\n"
- "Drop header: %s\n"
- "Keep tail: %s\n"
- "Convert text: %s\n"
- "Text files only: %s\n"
- "Attach comments: %s\n"
- "Verbose mode is obviously turned on.\n",
- InputName,
- DumpFiles ? "yes (copy each file in image to a real file)" :
- "no (just do a catalog listing)",
- NamePrefix,
- DropHeader ? "yes (remove header bytes from binary and BASIC files)" :
- "no (copy data as-is from start of file in disk image)",
- KeepTail ? "yes (keep garbage past end of file marker)" :
- "no (remove garbage past end of file)",
- ConvertText ? "yes (CR to LF and strip high bit in text files)" : "no",
- TextFilesOnly ? "yes (dump only does text files)" :
- "no (dump does all files)",
- NoComment ? "no (attach comments to dumped files)" : "yes");
-
- return TRUE;
- }
-
-
-
- /******************************************************************************
- * Read the command line arguments and set up various things. Returns TRUE
- * if successful. FALSE if not started from the command line etc.
- */
-
- BOOL ParseArguments (void)
- {
- LONG ArgumentValues [NUMRDARGS];
- struct TagItem DoneTag = {TAG_DONE, 0};
- LONG ErrorCode;
- struct RDArgs *RDArgParametersPntr;
- BOOL ReturnCode;
-
- if (_WBenchMsg != NULL)
- return FALSE; /* If started from the workbench, no command line exists. */
-
- RDArgParametersPntr = AllocDosObject (DOS_RDARGS, &DoneTag);
- if (RDArgParametersPntr == NULL)
- {
- printf ("Out of memory for allocating RDARGS structure.\n");
- return FALSE;
- }
-
- RDArgParametersPntr->RDA_ExtHelp = (char *) HelpText;
-
- memset (ArgumentValues, 0, sizeof (ArgumentValues));
-
- ReturnCode = (NULL != ReadArgs ((STRPTR) RDArgsTemplate,
- ArgumentValues, RDArgParametersPntr));
-
- if (ReturnCode)
- ReturnCode = ExamineArguments (ArgumentValues);
- else
- {
- ErrorCode = IoErr ();
- strcpy (ErrorMessage, ": ?");
- Fault (ErrorCode, (char *) "", ErrorMessage, sizeof (ErrorMessage));
- printf ("Oops%s, please use \"A2DiskDump ?\" for\n"
- "help and type a second \"?\" to get even more help.\n", ErrorMessage);
- }
-
- FreeArgs (RDArgParametersPntr);
- FreeDosObject (DOS_RDARGS, RDArgParametersPntr);
-
- return ReturnCode;
- }
-
-
-
- /******************************************************************************
- * Utility function for getting a pointer into the DiskImage array starting
- * at the given track and sector. If the track or sector is invalid, it
- * will return NULL.
- */
-
- void * DiskImageAtSector (unsigned char Track, unsigned char Sector)
- {
- if (Track >= 35)
- {
- printf ("Bad track requested for track $%02X, sector $%X.\n",
- (int) Track, (int) Sector);
- return NULL;
- }
-
- if (Sector >= 16)
- {
- printf ("Bad sector requested for track $%02X, sector $%X.\n",
- (int) Track, (int) Sector);
- return NULL;
- }
-
- /* Track and sector both zero means end of list of sectors etc,
- normally it won't appear in actual use. */
-
- if (Track == 0 && Sector == 0)
- return NULL;
-
- return DiskImage + (Track * 16L * 256L + Sector * 256L);
- }
-
-
-
- /******************************************************************************
- * Given a pointer to the first track/sector list's sector, builds a list
- * of all the sectors in the file, stored in the global CompleteSectorList.
- * Returns TRUE if successful, FALSE if something went wrong.
- */
-
- BOOL BuildSectorList (TSPair FirstTrackSectorList)
- {
- TSPair CurrentTSListTS;
- int i, j;
- TrackSectorList *TSListPntr;
- int WriteIndex;
-
- CurrentTSListTS = FirstTrackSectorList;
- NumberOfSectorsInCompleteSectorList = 0;
- memset (CompleteSectorList, 0, sizeof (CompleteSectorList));
-
- while (CurrentTSListTS.track != 0 || CurrentTSListTS.sector != 0)
- {
- if (Verbose)
- printf ("Processing track/sector list at $%02X/%X:\n",
- (int) CurrentTSListTS.track, (int) CurrentTSListTS.sector);
-
- TSListPntr = DiskImageAtSector (CurrentTSListTS.track,
- CurrentTSListTS.sector);
- if (TSListPntr == NULL)
- {
- printf ("Track/sector list in sector $%02X/%X doesn't exist, corrupt?\n",
- (int) CurrentTSListTS.track, (int) CurrentTSListTS.sector);
- return FALSE;
- }
-
- WriteIndex = TSListPntr->fileOffsetLow + 256L * TSListPntr->fileOffsetHigh;
-
- if (Verbose)
- {
- for (i = 0, j = 0; i < 122; i++)
- if (TSListPntr->list[i].track != 0 ||
- TSListPntr->list[i].sector != 0)
- j++;
- printf ("This TSlist has a sector offset of %u and has %d non-zero sectors.\n",
- (int) WriteIndex, j);
- }
-
- for (i = 0; i < 122; i++)
- {
- if (WriteIndex >= MAX_COMPLETE_TSLIST_SIZE)
- {
- printf ("Too many sectors (got %d so far) in the combined "
- "track/sector lists.\n", (int) WriteIndex);
- return FALSE;
- }
- CompleteSectorList [WriteIndex++] = TSListPntr->list[i];
- }
-
- if (WriteIndex > NumberOfSectorsInCompleteSectorList)
- NumberOfSectorsInCompleteSectorList = WriteIndex;
-
- /* Get the next TS list, use individual field transfers since the
- structure isn't at an even address so 68000 CPUs will crash if
- transfering it in one 16 bit quantity. */
-
- CurrentTSListTS.track = TSListPntr->nextTSList.track;
- CurrentTSListTS.sector = TSListPntr->nextTSList.sector;
- }
-
- /* Trim off the empty sectors at the end of the list. */
-
- i = NumberOfSectorsInCompleteSectorList;
- while (i > 0)
- {
- i--;
- if (CompleteSectorList[i].sector != 0 || CompleteSectorList[i].track != 0)
- {
- i++;
- break;
- }
- }
-
- if (Verbose)
- printf ("Track/sector list reduced from %d sector entries to %d.\n",
- (int) NumberOfSectorsInCompleteSectorList, (int) i);
-
- NumberOfSectorsInCompleteSectorList = i;
-
- if (Verbose)
- {
- printf ("Sectors used by file:");
- for (i = 0; i < NumberOfSectorsInCompleteSectorList; i++)
- printf (" $%02X/%X", (int) CompleteSectorList[i].track,
- (int) CompleteSectorList[i].sector);
- printf ("\n");
- }
-
- return TRUE;
- }
-
-
-
- /******************************************************************************
- * Dump a single file from the image. Returns TRUE if successful.
- */
-
- BOOL DumpSingleFile (FileEntry *FileEntryPntr)
- {
- int ActualFileLength;
- int AmountToWrite;
- BasicFileHeader *BasicFileHeaderPntr;
- BinaryFileHeader *BinaryFileHeaderPntr;
- char *BufferToWrite;
- char Comment [80];
- TSPair TSListTS;
- char FixedFileName [40];
- char Letter;
- int i, j;
- char OutputFileName [300];
- char ReadableFileName [40];
- int SizeToWrite;
- BOOL SkipFirstFourBytes;
- char TextBuffer [256];
- char *TextPntr;
-
- /* Watch out for odd aligned file info. */
-
- TSListTS.track = FileEntryPntr->firstTrackSectorList.track;
- TSListTS.sector = FileEntryPntr->firstTrackSectorList.sector;
-
- /* Copy the file name, stripping the Apple's high bit and
- changing control characters to readable characters. */
-
- for (i = 0; i < 30; i++)
- {
- Letter = FileEntryPntr->fileName[i];
- Letter = (Letter & 0x7F);
- if (Letter < 32)
- Letter = '_'; /* Replace control chars with underscore. */
- ReadableFileName[i] = Letter;
- if (Letter == ':' || Letter == '/' || Letter == '\\')
- Letter = '_'; /* Remove illegal characters for file names. */
- FixedFileName[i] = Letter;
- }
- ReadableFileName[30] = 0;
-
- /* Remove trailing blanks and other space things. */
-
- for (i = 29; i >= 0; i--)
- {
- if (!isspace (FixedFileName[i]))
- break;
- }
- FixedFileName[i+1] = 0;
-
- /* Deleted files use track $FF, last directory entry marked by $00/0. */
-
- if (TSListTS.track != 0xFF &&
- (TSListTS.track != 0 || TSListTS.sector != 0))
- {
- Comment[0] = (FileEntryPntr->fileType & 0x80) ? '*' : ' '; /* Locked */
- Comment[1] = 0;
- if ((FileEntryPntr->fileType & 0x7F) == 0)
- strcat (Comment, "T"); /* Text file */
- if (FileEntryPntr->fileType & 0x40)
- strcat (Comment, "L"); /* Alternate Type B - usually used for Lisa assembler source. */
- if (FileEntryPntr->fileType & 0x20)
- strcat (Comment, "a"); /* Type A - not the usual kind though. */
- if (FileEntryPntr->fileType & 0x10)
- strcat (Comment, "s"); /* Type S - another unusual kind. */
- if (FileEntryPntr->fileType & 0x08)
- strcat (Comment, "R"); /* Relocatable */
- if (FileEntryPntr->fileType & 0x04)
- strcat (Comment, "B"); /* Binary */
- if (FileEntryPntr->fileType & 0x02)
- strcat (Comment, "A"); /* Applesoft BASIC */
- if (FileEntryPntr->fileType & 0x01)
- strcat (Comment, "I"); /* Integer BASIC */
-
- sprintf (Comment + strlen (Comment), " %03d %s",
- (int) (FileEntryPntr->numberOfSectorsUsedLowByte +
- 256 *FileEntryPntr->numberOfSectorsUsedHighByte),
- ReadableFileName);
-
- if (Verbose)
- printf ("\nFixed file name is \"%s\".\n", FixedFileName);
-
- /* Make a list of all the sectors used by the file. */
-
- if (!BuildSectorList (TSListTS))
- return FALSE;
-
- /* Extract the header info for binary and BASIC files, and find the
- exact size of the file (BASIC and Binary length from header plus
- header size, position of first NUL for Text files). */
-
- ActualFileLength = NumberOfSectorsInCompleteSectorList * 256;
-
- if (FileEntryPntr->fileType & 0x04) /* Binary files. */
- {
- BinaryFileHeaderPntr = DiskImageAtSector (CompleteSectorList[0].track,
- CompleteSectorList[0].sector);
- if (BinaryFileHeaderPntr == NULL)
- return FALSE;
- sprintf (Comment + strlen (Comment), ",A$%X,L$%X",
- (int) (BinaryFileHeaderPntr->addressLow +
- 256 * BinaryFileHeaderPntr->addressHigh),
- (int) (BinaryFileHeaderPntr->lengthLow +
- 256 * BinaryFileHeaderPntr->lengthHigh));
- ActualFileLength = BinaryFileHeaderPntr->lengthLow +
- 256 * BinaryFileHeaderPntr->lengthHigh +
- 4 /* For header size */;
- }
-
- if (FileEntryPntr->fileType & 0x03) /* All BASIC types of files. */
- {
- BasicFileHeaderPntr = DiskImageAtSector (CompleteSectorList[0].track,
- CompleteSectorList[0].sector);
- if (BasicFileHeaderPntr == NULL)
- return FALSE;
- sprintf (Comment + strlen (Comment), ",L$%X",
- (int) (BasicFileHeaderPntr->lengthLow +
- 256 * BasicFileHeaderPntr->lengthHigh));
- ActualFileLength = BasicFileHeaderPntr->lengthLow +
- 256 * BasicFileHeaderPntr->lengthHigh +
- 2 /* For header size */;
- }
-
- if ((FileEntryPntr->fileType & 0x7F) == 0) /* Text files. */
- {
- /* Find the end of the file - first NUL in the file. */
-
- for (i = 0; i < NumberOfSectorsInCompleteSectorList; i++)
- {
- if (CompleteSectorList[i].track == 0 &&
- CompleteSectorList[i].sector == 0)
- {
- /* Found an unallocated sector, equivalent to all zeroes. */
-
- ActualFileLength = 256 * i;
- break;
- }
-
- /* Look inside a data sector for the NUL. */
-
- TextPntr = DiskImageAtSector (CompleteSectorList[i].track,
- CompleteSectorList[i].sector);
- if (TextPntr == NULL)
- return FALSE;
- for (j = 0; j < 256; j++)
- {
- if (TextPntr[j] == 0)
- break;
- }
- if (j < 256)
- {
- ActualFileLength = 256 * i + j;
- break;
- }
- }
- }
-
- if (KeepTail)
- SizeToWrite = NumberOfSectorsInCompleteSectorList * 256;
- else
- SizeToWrite = ActualFileLength;
-
- if (DropHeader && (FileEntryPntr->fileType & 0x04))
- {
- SizeToWrite -= 4; /* Skip 4 byte header when dumping binary files. */
- SkipFirstFourBytes = TRUE;
- }
- else
- SkipFirstFourBytes = FALSE;
-
- /* Try to open the output file. */
-
- strcpy (OutputFileName, NamePrefix);
- strcat (OutputFileName, FixedFileName);
-
- if (DumpFiles &&
- ((FileEntryPntr->fileType & 0x7F) == 0 || !TextFilesOnly) &&
- strlen (FixedFileName) > 0 && SizeToWrite > 0)
- {
- if (Verbose)
- printf ("Opening output file \"%s\".\n", OutputFileName);
-
- OutputFile = fopen (OutputFileName, "wb");
-
- if (OutputFile == NULL)
- printf ("Unable to open file \"%s\".\n", OutputFileName);
- }
- else
- {
- if (Verbose)
- printf ("Not writing \"%s\".\n", OutputFileName);
- }
-
- if (OutputFile != NULL)
- {
- for (i = 0; SizeToWrite > 0; i++)
- {
- if (i >= NumberOfSectorsInCompleteSectorList ||
- (CompleteSectorList[i].track == 0 &&
- CompleteSectorList[i].sector == 0))
- {
- /* Found an unallocated sector, write all zeroes. */
-
- BufferToWrite = ZeroesBuffer;
- }
- else
- {
- BufferToWrite = DiskImageAtSector (CompleteSectorList[i].track,
- CompleteSectorList[i].sector);
- if (BufferToWrite == NULL)
- return FALSE;
- }
-
- AmountToWrite = 256;
-
- if (SkipFirstFourBytes)
- {
- SkipFirstFourBytes = FALSE;
- BufferToWrite += 4;
- AmountToWrite -= 4;
- }
-
- if (AmountToWrite > SizeToWrite)
- AmountToWrite = SizeToWrite;
-
- if (ConvertText && (FileEntryPntr->fileType & 0x7F) == 0)
- {
- /* Strip the high bit and convert CR to LF. */
-
- for (j = 0; j < AmountToWrite; j++)
- {
- Letter = (0x7F & BufferToWrite[j]);
- if (Letter == '\r') /* Convert carriage return to line feed. */
- Letter = '\n';
- TextBuffer[j] = Letter;
- }
- BufferToWrite = TextBuffer;
- }
-
- if (fwrite (BufferToWrite, 1, AmountToWrite, OutputFile) !=
- AmountToWrite)
- {
- printf ("Error while writing data to \"%s\".\n", OutputFileName);
- return FALSE;
- }
-
- SizeToWrite -= AmountToWrite;
- }
-
- fclose (OutputFile);
- OutputFile = NULL;
-
- /* Attach the comment text to the file. */
-
- if (!NoComment)
- SetComment (OutputFileName, Comment);
- }
-
- /* Print the Apple II style catalog listing line. */
-
- printf ("%s\n", Comment);
- }
- else
- {
- if (Verbose)
- {
- if (TSListTS.track == 0xFF)
- printf ("Ignoring deleted file: \"%s\"\n", ReadableFileName);
- else if (TSListTS.track != 0)
- printf ("A weird file with TSList on track $%02X: \"%s\"\n",
- (int) TSListTS.track, ReadableFileName);
- /* else track 0 - an empty file entry, don't print. */
- }
- }
-
- return TRUE;
- }
-
-
-
- /******************************************************************************
- * This function opens the disk image file and spins through the catalog
- * entries in it, calling a file extractor subroutine for each file inside it.
- * Returns TRUE if successful, FALSE if something went wrong.
- */
-
- BOOL BreakUpDiskImage (void)
- {
- unsigned short BytesPerSector;
- BOOL Corrupt;
- Directory *DirectoryPntr;
- TSPair DirectoryTS;
- FileEntry *FileEntryPntr;
- int FileNumber;
- int ImageFileSize;
- VTOC *VTOCPntr;
-
- if (Verbose)
- printf ("Opening disk image file...\n");
-
- InputFile = fopen (InputName, "rb");
- if (InputFile == NULL)
- {
- printf ("Unable to open \"%s\".\n", InputName);
- return FALSE;
- }
-
- if (fseek (InputFile, 0, SEEK_END) == -1)
- {
- printf ("Unable to seek to end of file \"%s\".\n", InputName);
- return FALSE;
- }
-
- ImageFileSize = ftell (InputFile);
- if (ImageFileSize == -1)
- {
- printf ("Unable to find size of file \"%s\".\n", InputName);
- return FALSE;
- }
-
- if (fseek (InputFile, 0, SEEK_SET) == -1)
- {
- printf ("Unable to seek to beginning of file \"%s\".\n", InputName);
- return FALSE;
- }
-
- if (ImageFileSize != sizeof (DiskImage))
- {
- printf ("File \"%s\" isn't a disk image, its size is %d, not %d.\n",
- InputName, ImageFileSize, sizeof (DiskImage));
- return FALSE;
- }
-
- if (Verbose)
- printf ("Reading disk image file into memory...\n");
-
- if (fread (DiskImage, sizeof (DiskImage), 1, InputFile) != 1)
- {
- printf ("Unable to read the whole disk image into memory from \"%s\".\n",
- InputName);
- return FALSE;
- }
-
- fclose (InputFile);
- InputFile = NULL;
-
- /* Do a little bit of sanity checking on the volume table of contents. */
-
- Corrupt = FALSE;
- VTOCPntr = DiskImageAtSector (0x11, 0);
-
- if (VTOCPntr->dosVersion != 3)
- {
- printf ("DOS version %d isn't 3.\n", (int) VTOCPntr->dosVersion);
- Corrupt = TRUE;
- }
-
- VolumeNumber = VTOCPntr->volumeNumber;
- if (Verbose)
- printf ("Disk volume #%d.\n", VolumeNumber);
-
- if (VTOCPntr->tracksPerDisk != 35)
- {
- printf ("The VTOC claims %d tracks per disk, it needs to be 35.\n",
- (int) VTOCPntr->tracksPerDisk);
- Corrupt = TRUE;
- }
-
- if (VTOCPntr->sectorsPerTrack != 16)
- {
- printf ("The VTOC claims %d sectors per track, it needs to be 16.\n",
- (int) VTOCPntr->sectorsPerTrack);
- Corrupt = TRUE;
- }
-
- BytesPerSector =
- VTOCPntr->bytesPerSectorLowByte +
- 256 * VTOCPntr->bytesPerSectorHighByte;
- if (BytesPerSector != 256)
- {
- printf ("The VTOC claims %d bytes per sector, it needs to be 256.\n",
- (int) BytesPerSector);
- Corrupt = TRUE;
- }
-
- if (Corrupt)
- {
- printf ("This doesn't appear to be an Apple DOS 3.3 format disk.\n");
- return FALSE;
- }
-
- DirectoryTS.track = VTOCPntr->firstDirectory.track;
- DirectoryTS.sector = VTOCPntr->firstDirectory.sector;
-
- while (TRUE)
- {
- if (Verbose)
- printf ("Now doing directory sector $%02X/%X:\n",
- (int) DirectoryTS.track, (int) DirectoryTS.sector);
-
- DirectoryPntr = DiskImageAtSector (DirectoryTS.track, DirectoryTS.sector);
- if (DirectoryPntr == NULL)
- break; /* Reached end of sector list (0,0) or bad track/sector. */
-
- for (FileNumber = 0; FileNumber < 7; FileNumber++)
- {
- FileEntryPntr = DirectoryPntr->fileEntry + FileNumber;
-
- if (!DumpSingleFile (FileEntryPntr))
- return FALSE;
- }
-
- /* Get next directory sector, watch out for
- odd alignment of the TSPair field. */
-
- DirectoryTS.track = DirectoryPntr->nextDirectory.track;
- DirectoryTS.sector = DirectoryPntr->nextDirectory.sector;
- }
-
- return TRUE;
- }
-
-
-
- /******************************************************************************
- * Called at program exit to clean up globally allocated things.
- */
-
- void CleanUp (void)
- {
- if (InputFile != NULL)
- {
- fclose (InputFile);
- InputFile = NULL;
- }
-
- if (OutputFile != NULL)
- {
- fclose (OutputFile);
- OutputFile = NULL;
- }
- }
-
-
-
- /******************************************************************************
- * The usual main program entry point.
- */
-
- int main (void)
- {
- atexit (CleanUp);
-
- if (!ParseArguments ())
- return 10; /* Failure somewhere, or run from Workbench, not shell. */
-
- if (!BreakUpDiskImage ())
- return 20; /* Something went wrong. */
-
- if (Verbose)
- printf ("Done!\n");
-
- return 0; /* Success! */
- }
-